feat: Argus Console (TUI) + browser viewer modernization — interactive console, live intelligence, charts, formal PDF report#261
Conversation
Turn the terminal viewer from a single-run reader into a project explorer that can also drive scans, and extract the run-finding logic both viewers were going to need into one place. Terminal viewer (argus view terminal): - Runs sidebar (``b``): lists the scan runs discovered next to the current one — newest first, each row tagged with its worst-finding glyph and finding count. Auto-reveals when more than one run exists. Selecting a run loads it in place, preserving the active filters / sort and resetting the (now-stale) multi-select. - In-app scan runner (``R``): prompts for an optional scanner + path, runs ``argus scan`` as a subprocess streamed into an overlay, and reloads the freshly-written run when it finishes. Re-invokes the running interpreter (sys.executable -m argus) so a venv'd viewer scans with that venv's argus and scanners. - Context menus now open at the cursor (clamped to stay on screen) instead of screen-centre. Refactor: - New argus.core.run_discovery owns discover_runs / list_directory / peek_run_stats / is_within / RESULTS_FILENAME (UI-free). The browser viewer's _collect_recent_scans / _list_directory / _is_within now delegate to it, deduping ~200 lines and guaranteeing the two interfaces agree on what counts as a run (incl. latest/ symlink de-dup). peek_run_stats distinguishes a malformed results file (count None) from a valid empty scan (0). Pure logic (run discovery, row formatting, argv building, menu-offset clamping) is unit-tested directly; the Textual widgets are exercised via the stubbed-textual loader. +55 tests, coverage 92%. Docs and .ai/architecture.yaml updated.
🚀 Release Preview📦 Version UpdateCurrent: 📋 Changelog1.5.0 (2026-06-14)Features
Bug Fixes
Maintenance
Documentation
Tests
🔍 Version Reference Coverage✅ Version refs found: 336 across 115 files All covered by release-it config. ✅ Actions that would be performed
This preview is generated by running Last updated: 6/14/2026, 1:19:10 AM | Commit: 9b7a5ab | View Run |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
🔒 Argus Container Security ScanBranch: 📊 Combined Findings Summary
Scanned: 5 containers | Build Failures: 0 📦 Container Breakdown
🔍 Detailed Findings by Container🚨 cli - 108 vulnerabilities (45 unique)Image: Combined (Deduplicated)
🔷 Trivy Scanner (108 findings, 43 unique)
...and 58 more ⚓ Grype Scanner (0 findings, 0 unique)✅ No vulnerabilities detected by Grype ✅ scanner-bandit - 0 vulnerabilities (0 unique)Image: Combined (Deduplicated)
🔷 Trivy Scanner (0 findings, 0 unique)✅ No vulnerabilities detected by Trivy ⚓ Grype Scanner (0 findings, 0 unique)✅ No vulnerabilities detected by Grype ✅ scanner-mumps - 0 vulnerabilities (0 unique)Image: Combined (Deduplicated)
🔷 Trivy Scanner (0 findings, 0 unique)✅ No vulnerabilities detected by Trivy ⚓ Grype Scanner (0 findings, 0 unique)✅ No vulnerabilities detected by Grype 🚨 scanner-opengrep - 106 vulnerabilities (49 unique)Image: Combined (Deduplicated)
🔷 Trivy Scanner (106 findings, 48 unique)
...and 56 more ⚓ Grype Scanner (0 findings, 0 unique)✅ No vulnerabilities detected by Grype
|
| 🚨 Critical | 🟡 Medium | 🔵 Low | Total | Unique | |
|---|---|---|---|---|---|
| 0 | 12 | 6 | 0 | 18 | 18 |
🔷 Trivy Scanner (18 findings, 18 unique)
| CVE | Severity | Package | Version | Fixed |
|---|---|---|---|---|
| CVE-2026-32280 | stdlib | v1.26.1 | 1.25.9, 1.26.2 | |
| CVE-2026-32281 | stdlib | v1.26.1 | 1.25.9, 1.26.2 | |
| CVE-2026-32283 | stdlib | v1.26.1 | 1.25.9, 1.26.2 | |
| CVE-2026-33810 | stdlib | v1.26.1 | 1.26.2 | |
| CVE-2026-33811 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-33814 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-39820 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-39823 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-39825 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-39836 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-42499 | stdlib | v1.26.1 | 1.25.10, 1.26.3 | |
| CVE-2026-42504 | stdlib | v1.26.1 | 1.25.11, 1.26.4 | |
| CVE-2026-27145 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.11, 1.26.4 |
| CVE-2026-32282 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.9, 1.26.2 |
| CVE-2026-32288 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.9, 1.26.2 |
| CVE-2026-32289 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.9, 1.26.2 |
| CVE-2026-39826 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.10, 1.26.3 |
| CVE-2026-42507 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.11, 1.26.4 |
⚓ Grype Scanner (0 findings, 0 unique)
✅ No vulnerabilities detected by Grype
Generated by Argus
…unner Capture the new TUI states as committed SVGs (11–14) alongside the existing view-terminal screenshots, embed them in docs/view-terminal.md, and extend scripts/docsite/capture_view_terminal.py to regenerate them so they can't drift: - 11-runs-sidebar — runs sidebar revealed beside the findings list - 12-scan-runner-prompt — the "Run a scan" prompt overlay - 13-scan-runner-output — streamed scan output + "complete" status - 14-context-menu-anchored — context menu opening at the cursor The runs-sidebar / runner / anchored-menu states use a deterministic two-run synthetic fixture (the real container scans land in separate parents and don't exercise sibling discovery; the runner-output stream is stubbed for a stable capture). Docsite builds clean with all four refs resolving.
Capture the TUI's evolution from post-scan viewer into a terminal-native control centre (scan → triage → fix → configure, and eventually what bare `argus` launches in a TTY) as a phased epic, matching the SDK-ROADMAP format: - Phase 0 — explorer + scan runner (shipped this PR, #261) - Phase 1 — vulnerability mitigation ("Fix"): Tier 1 deterministic/OSS, Tier 2 AI-assisted (opt-in, diff-only, reuses scn/ai.py + [ai] extra) - Phase 2 — config editor screen (form + live validation + surfaced docs) - Phase 3 — init wizard screen - Phase 4 — Console home + bare-`argus` entry (TTY-gated; needs an ADR) Documents the load-bearing backward-compat contract (bare `argus` only goes interactive on a TTY; every subcommand and all headless/CI use is unchanged), the screens-over-UI-free-core architecture, and the open product decisions per phase. Linked from SDK-ROADMAP.md's in-flight initiatives.
Make bare `argus` (interactive TTY only) open the Argus Console: a screen-based Textual home for the whole local workflow, with a herdr-style settings system. Builds on the runs-explorer / scan-runner foundations. Home (console.py + console_model.py): - ASCII wordmark + tagline (fade honours motion settings / ARGUS_NO_ANIMATION), a status line (config presence + latest run label / count / worst severity via run_discovery), and a launcher menu: Run a scan / View findings / Configure / Initialize / Settings / Help & docs / Quit. - Scan reuses the in-app RunScanScreen; Configure opens argus.yml in $EDITOR/$VISUAL; Initialize streams `argus init`; View findings hands off to the existing viewer (`argus view`) and returns. Settings (full Screen, herdr-style) + console_config.py (UI-free): - Theme (built-in Textual themes + a bespoke argus-dark), accent colour, animations, reduced-motion, notifications. Theme/accent preview live; persisted to $XDG_CONFIG_HOME/argus/console.yml (pyyaml only). Settings is a full Screen, not a ModalScreen — live re-theming under a modal hits a Textual 8.x NoneType-visual render bug. Entry point (cli._run_bare_argus): - Opens the Console only when stdout AND stdin are a TTY; piped / CI / missing-[terminal] falls back to today's `--help`, so headless use is unchanged. `argus view` still deep-links to findings. Pure logic (settings, model, status, argv) is unit-tested in CI without the [terminal] extra; the Textual screens are covered via the stubbed-textual loader and verified end-to-end with a real-Textual Pilot run. Docs: docs/console.md + regenerable screenshots (scripts/docsite/capture_console.py); CONSOLE-ROADMAP Phase 4 shell marked shipped; .ai/architecture.yaml updated.
…R review) Address PR #262 review feedback: - Theme: argus-dark now mirrors the Argus website / browser viewer brand (deep green-black base, primary green #84b852, lime accent #dbe64c) instead of the placeholder cyan. Default accent is the brand green; severity warning/error tokens match the browser's med/crit. Non-default accents still recolour the primary/accent. - Centering: the wordmark is now a full-width, text-align:center banner in a fixed-width centred column, so it sits dead-centre over the menu (it was left-of-centre). - Tagline: "perception is protection". - Wordmark font: ansi_regular (clean flat blocks) replaces the heavier ansi_shadow. Committed screenshots regenerated; tests + lint updated/green.
The ANSI block-letter banner relies on `█` glyphs tiling seamlessly; GitHub's image proxy renders the committed SVG with a font that strips the rows apart, so the wordmark looked mangled on the PR. Switch the console screenshots to raster PNG (pixel-identical everywhere), point docs/console.md at them, and update capture_console.py to rasterise its SVG capture to PNG (rsvg-convert / cairosvg / qlmanage, whichever is on PATH; Pillow trims padding).
The banner used text-align:center, which centres each ASCII row independently — since the rows are rstripped to different widths, the shorter rows drifted right (the "row two indented too far" artifact). Render the banner as a width:auto block wrapped in a Center container so the rows keep their figlet alignment and the whole block centres over the menu. Committed PNGs regenerated.
feat(console): Argus Console — bare-argus home launcher + live settings
Phase 1 of the Console roadmap — let users resolve findings, not just read them, with no AI and no new runtime deps. Core (argus/core/remediation.py, UI-free): - propose(finding, *, repo_root) / apply() / is_fixable() turn a dependency finding (package + fixed_version + purl) into a reviewable fix. Tier-1 covers dependency version bumps for pip (requirements*.txt) and npm (package.json): locate the package, emit a unified diff bumping it to the fixed version, preserving the user's spec style (== stays pinned, >= keeps its operator, bare name -> >=). PEP 503 name normalization for pip; caret/tilde preserved for npm. Falls back to the ecosystem upgrade command (shown, never auto-run) when no manifest line matches. - Diff-/command-first: nothing touches the working tree until apply(); apply is idempotent and refuses to write command-only remediations. TUI (argus view terminal): - F (shift+f) -> FixScreen: previews the proposed diff(s), applies on a/Enter, batch over the multi-select set. An "Apply fix" item appears in the right-click context menu for fixable findings. After applying, re-run the scan (R) to confirm. Pure logic unit-tested in CI without the [terminal] extra; the TUI wiring is covered via the stubbed-textual loader and verified end-to-end with a real-Textual Pilot run. Docs + CONSOLE-ROADMAP (Phase 1 Tier-1) + .ai/architecture.yaml updated. +35 tests.
feat(remediation): Tier-1 deterministic Fix engine + TUI Fix overlay
Phase 2 of the Console roadmap. The home menu's Configure entry now opens a form editor (ConfigScreen) instead of shelling out to $EDITOR: scanner on/off toggles and the bounded choice settings (severity_threshold, execution backend/pull_policy, view cve_source/open_location) are listed with their current value and a one-line description; enter cycles a value, s validates + writes, esc discards. Edits go through config_editor.py — a UI-free, textual-free module so it's covered in CI without the [terminal] extra. Write-back is comment-preserving: set_value walks the file indentation-aware to the matched key: value line and rewrites only its value, keeping key, indentation, and any trailing inline comment. This resolves the roadmap's open YAML write-back decision in favour of targeted line edits over a ruamel.yaml round-trip — no new dependency, at the cost of only editing settings already present in the file. Before saving, the result is re-validated against argus.core.schema.validate_config and refused on any error-level issue. When no argus.yml exists, Configure nudges toward Initialize and falls back to $EDITOR/$VISUAL so a file can still be created by hand.
feat(console): form editor for argus.yml (Configure screen)
Phase 3 of the Console roadmap. The home menu's Initialize entry now opens an in-app wizard (InitScreen) instead of shelling out to `argus init`: it detects the project, shows what it found plus a tool-readiness line, lists the proposed scanners as toggles, and writes argus.yml. `w` writes; `r` writes and hands straight to the Phase-0 scan runner for the first scan; `esc` cancels. An existing argus.yml requires a confirming second keypress before it's overwritten — mirroring `argus init`'s --force guard, never a silent clobber. The wizard reimplements no detection logic. init_wizard.py is a UI-free, textual-free frontend over the pure functions in argus.init (detect_project, generate_config, _extract_enabled_scanners, _check_local_readiness): build_plan returns an InitPlan (detected categories, proposed scanners, generated YAML, readiness) and write_config enforces the overwrite guard. Scanner toggles reuse the Phase-2 config_editor over the generated YAML, so toggling and the comment-preserving write share one implementation. The signal-to-label map moves to argus.init.SIGNAL_LABELS so the CLI summary and the wizard render identical names. With Init now in-app, the _HANDOFF_INIT sentinel and its subprocess hand-off in launch() are removed.
feat(console): in-app init wizard (Initialize screen)
Adds Part II to the Console roadmap — the eight phases that take the
Console from "exists" to best-in-class for security tooling, since almost
every scanner (trivy/grype/semgrep/snyk/osv-scanner/gitleaks) is
non-interactive:
- Phase 5 Modern navigation essentials (command palette, fuzzy filter,
OSC 8 hyperlinks, OSC 52 clipboard)
- Phase 6 Live vulnerability intelligence (EPSS + CISA KEV + OSV/GHSA
enrichment, risk re-ranking) — the headline
- Phase 7 Triage at scale (bulk suppress + OpenVEX writeback + audit trail)
- Phase 8 Visual analytics (textual-plotext trend/scanner/severity charts)
- Phase 9 Inline graphics (Kitty/Sixel via textual-image, ASCII fallback)
- Phase 10 AI triage assistant (reuse argus/scn/ai.py, Ollama default,
no key required) — foundation
- Phase 11 Console on the web (textual serve, localhost + token gated) — ADR
- Phase 12 Reachability-aware prioritization (heuristic first cut; true
call-graph reachability stays roadmap-only research)
Each phase keeps the Part I split (logic in UI-free argus/core/, thin
screens) and every network/AI feature is opt-in and offline-degrading so
the default argus experience needs no key, daemon, or egress. Build order
and sequencing documented.
Three issues surfaced test-driving the Console's Configure screen: 1. Long keys ran into the value column — `reporting · severity_threshold` (30 chars) overflowed the fixed `:<26` label width and rendered as "severity_thresholdcritical". Column widths are now sized to the widest label/value (new UI-free `config_editor.row_display`), so nothing collides regardless of key length. 2. Bounded-choice (enum) settings cycled one value per Enter press. They now open a `_ChoiceScreen` dropdown picker listing the allowed values (current one marked), so the user selects directly. Enum rows carry a ▾ affordance; on/off toggles still flip in place on Enter. 3. After a change, the OptionList is rebuilt via recompose but was never re-focused, so arrow keys and Enter silently died until a mouse click. `_restore_cursor` now re-focuses the rebuilt list — fixed on the Configure, Settings, and Init screens (all shared the pattern). - row_display is UI-free + unit-tested (alignment, no-overlap regression, ▾ affordance); the picker + focus wiring follow the existing modal pattern. 302 terminal-viewer tests green.
fix(console): Configure screen — column overlap, enum picker, focus loss
Two issues from test-driving: 1. Bare `argus` → "View findings" flickered straight back to the home screen. The terminal loader's `locate_results` only looked for `argus-results/argus-results.json`, but `argus scan` writes to a timestamped subdir (`argus-results/<ts>/`) and maintains a `latest` symlink — so the file was never found, `load_summary` raised, and the findings app exited on mount. `locate_results` now resolves a directory the same way the browser viewer does: direct drop → `latest/` → newest discovered run (via `discover_runs`). A file path and a direct `argus-results.json` still win, and `None` resolves against the conventional `./argus-results` home. 2. The browser **Report** nav link now opens in a new tab (`target="_blank"`, with a ↗ hint). The report is a standalone print document, so opening it in place forced a Back press to return to the triage view. Regression tests cover latest-symlink, newest-run, no-symlink, and direct-drop-wins resolution.
…ion-and-report-tab fix(viewers): resolve latest run for View findings + open report in new tab
… (O) When the findings viewer can't resolve a scan in the launch directory it now opens a filesystem picker instead of exiting (which read as the "View findings flickers back to the Console home" report). The same picker is available on demand via `O` (shift+o) to traverse to another project's results or an archived folder without relaunching — it re-roots the runs sidebar at the opened scan. - argus/viewers/terminal/results_picker.py — UI-free row formatting + id encode/decode over run_discovery.list_directory (dirs + results files only; scan dirs flagged with a finding-count peek). Fully tested. - ResultsPickerScreen — navigates one level at a time (backspace = up), loads a scan-bearing dir / results file on select. - BrowseApp: extracted `_try_load` (one loader shared by on_mount, the runs sidebar, the scan runner, and the picker); on_mount routes the no-scan case to `_prompt_for_results`; `_switch_run(..., update_root=)` re-roots discovery when opening from elsewhere; `O` keybind + command-palette entry + help. `O` is capital (lowercase `o` is "open last export"), matching the capital-for-major-action convention (R / F / S / D). Docs + .ai updated.
feat(view): directory picker — open a scan from anywhere (O) + no-scan fallback
Surface "can I scan right now, and with what?" before the user hits a wall like "Docker isn't running" (which is what prompted this). The home gets a single go/no-go chip — ● ready / ▲ warning / ✖ blocked — with a short headline; press `d` to expand it into the full breakdown. Three checks (argus.core.system_status, UI-free, injectable probes): - Docker daemon — running / installed-but-stopped / absent. Only *blocking* when the effective execution.backend needs it with no local fallback (so a stopped Docker on an all-local setup is a warning, not a hard stop; backend: docker with Docker down is blocking). - Local tools — how many common scanners are runnable as local binaries. - Scanner images — best-effort: cached Argus image digests vs published release digests (genuine vs rebuilt/overridden), skipped when Docker is off. - compute_status() is pure over injected probes → fully unit-tested without Docker or installed tools; the three subprocess probes are the only un-covered edges. Computed in a background worker so the home paints instantly; SystemStatusScreen renders the detail + remediation. - `d` keybind on the home; docs + .ai updated.
feat(console): system-readiness chip on the home screen (Docker / tools / image digests)
Settings → Theme now opens a dropdown of all themes instead of cycling one step per Enter: arrow up/down to see each theme applied live, Enter to keep it, Esc to revert to the one you started on. - ThemePickerScreen (a full Screen, not a ModalScreen — applying a theme under a ModalScreen hits the Textual 8.x NoneType-visual bug, the same reason SettingsScreen is a full screen): highlighting a theme previews it via app.apply_theme(); Enter dismisses keeping it, Esc restores the original. - ConsoleSettings.with_value(key, value) — UI-free setter for a *specific* value (vs advance()'s cycle), normalized so an out-of-range theme snaps to the default. - SettingsScreen routes the Theme row to the picker; accent + toggles keep their cycle-on-Enter behaviour. - Tests: with_value (set / invalid-snaps / unknown-key / immutability) + ThemePickerScreen (preview applies live, Enter keeps, Esc reverts). Docs + .ai updated.
feat(console): live-preview theme dropdown in Settings
Three spots left the TUI "in the dark" during brief synchronous work. Each now gives immediate feedback instead of a frozen screen: - Initialize — `build_plan` (a filesystem walk) ran on the UI thread before the wizard opened. It now runs in a worker behind an immediate "Detecting project…" toast, so the home stays live; the wizard opens when detection finishes (_open_init → worker → _show_init). - Run switching — selecting a run (sidebar / picker / post-scan reload) loaded the results + rebuilt the table synchronously. It now shows the DataTable's built-in spinner (loading = True) while the load runs in a worker, applying the rows back on the UI thread (_switch_run → _finish_switch) so the spinner actually animates. - Scan — RunScanScreen now prints "⏳ Starting argus scan…" immediately, before the subprocess produces its first line, so the pane isn't blank during spawn + scanner startup. Test fixtures run the workers synchronously (run_worker / call_from_thread stubs) so the existing state assertions still hold; production runs them threaded.
feat(view): loading feedback for init, scan startup, and run switching
…eport The README predated the whole interactive-surface + reporting work and undersold it: the Console/TUI was described as a "findings explorer", the browser viewer was one bullet, the formal PDF report wasn't mentioned, and there were no product screenshots (plus a duplicated TUI bullet and a stale scanner list). Rewrite around a Find it → Triage it → Prove it story: - A screenshot showcase up top (Console home · browser dashboard · PDF report), all relative paths so they render on GitHub. - "Triage it" section for the Console (in-app scan/fix/triage, EPSS/KEV intel, OpenVEX suppress, AI explain, runs sidebar + filesystem picker, command palette, system-readiness chip, theme picker) and the browser viewer (charts, risk column, light/dark). - "Prove it" section for the one-click PDF report + cosign provenance + signed OpenVEX attestations. - Refreshed scanner table (adds gosec, MUMPS, supply-chain, dependency review, SCN compliance, linters), de-duped Features, tightened GHES / config / MCP into scannable sections. No content claims beyond what ships today; version pins unchanged (release-it manages them).
docs(readme): bold refresh — showcase the Console, browser & PDF report
The browser UI was renamed from `argus serve` to `argus view browser`, but
the old name lingered — most visibly in the "No scan loaded" splash ("Point
argus serve at a results directory…"), plus template/JS/CSS comments, two
console.warn prefixes, and the docs troubleshooting (which still referenced
the removed `serve` command + a non-existent `[serve]` extra).
Swept argus/viewers/browser/ to the current command name and corrected the
two stale troubleshooting entries in docs/view-browser.md. Text/comment only;
no behaviour change.
… sections The hero tagline is now three in-page links jumping to the matching sections, via explicit <a id> anchors (robust against GitHub's emoji/em-dash slug munging on the "✦ … —" headers).
The bold README refresh dropped the AICaC adoption badge. The AICaC workflow has update-badge: true, so it then tried to regenerate + push the badge back to the branch and failed at the push (checkout uses persist-credentials: false → no push creds → git exit 128). Restoring the badge means the action finds it up to date and never attempts the push.
The architecture-map modal renders a scanner's `purpose` field. MUMPS's was ~6.3k chars (every rule M001–M219, all config knobs, FP analysis, validation corpora, parser pin, phase roadmap) — an unreadable wall versus 15–110 chars for every other scanner. Cut it to a one-liner in the same voice as gosec (what it covers + a few example sink categories), matching the rest of the map. The full rule list + config still live in docs/scanners.md and argus/scanners/mumps/, which is where that depth belongs.
Description
This branch began as a terminal-viewer UX tweak and grew into a full interactive-surface modernization of Argus, delivered as two phased epics that each landed sub-PR-by-sub-PR into this integration branch so the whole thing can be reviewed and tested as one before it reaches
main:argusnow opens (CONSOLE-ROADMAP.md).argus view browserfrom a read-only findings list into a charts-and-report surface, culminating in a formal, government-grade PDF vulnerability report (BROWSER-ROADMAP.md).Pure UI/UX + frontend work — no scanner, action, or schema changes. Every interactive surface keeps Argus's posture: the Console's scan/Fix/configure surfaces are terminal-only (the user's trusted shell); the browser viewer stays read-only /
127.0.0.1/ no-mutation.Console epic — phases folded in
argusentry (argus-darkbrand theme)argus.yml(comment-preserving, schema-validated)argus init)core/enrichment)Browser viewer & reporting epic — phases folded in
/findings(ARGUS_VIEW_ENRICH=1)/report(HTML) +/report.pdf(server-side WeasyPrint, opt-in[report])Deferred (not in this branch): Console Tier-2 AI fixes; browser B3 (URL-addressable views + dependency drilldown) and B5 (a11y / responsive polish).
Screenshots
Argus Console (TUI)
Console home — bare
argusopens the launcher: brandargus-darkwordmark, a project-status line (config presence + latest run / count / worst severity), and the menu (Scan / View findings / Configure / Initialize / Settings / Docs / Quit).Settings — live-previewed preferences (theme, accent, animations, reduced-motion, notifications), persisted to
~/.config/argus/console.yml.Runs sidebar (
b) + in-app scan runner (R) — every run next to the current one (worst-finding glyph + count); prompt → streamargus scaninto an overlay → reload the new run.Browser viewer
Executive dashboard — severity-accented stat cards, real inline-SVG charts (severity donut, findings-over-time trend, by-scanner bars), and the persistent sticky scan-context bar (project · commit · scan time · finding count) under the header.
Light mode — everything (charts, cards, context bar) is built on theme tokens and flips cleanly.
Command palette (Cmd/Ctrl-K) — fuzzy launcher over the page's nav; browser parity with the TUI's Ctrl+P.
Opt-in Risk column (EPSS + CISA KEV) —
ARGUS_VIEW_ENRICH=1adds a 0–100 risk score that re-ranks by real-world urgency (the KEV-flagged finding scores highest).Formal vulnerability report (
/report, one-click PDF) — the authoritative artifact: provenance & attestation (Argus version, source commit, cosign-verified scanner image digests, signed-attestation status), a PASS/FAIL verdict, charts, and the full findings inventory grouped by severity.Changes Made
argusentry)[report]extra)Details
Console (Phases 0–5) —
console.py(App + screens) over textual-freeconsole_model/console_config. Bareargusopens the Console only when stdout and stdin are a TTY (cli._run_bare_argus); piped / CI / no-[terminal]falls back to--help. Project explorer (runs sidebarb, in-app scan runnerR, cursor-anchored context menus) over the sharedcore/run_discovery.py. Tier-1 deterministic Fix engine (core/remediation.py, pip + npm bumps, diff preview, OSS-only). Configure (config_editor.py, comment-preserving + schema-validated). Initialize (init_wizard.pyover pureargus.init). Ctrl+P command palette.Console live-intelligence (Phases 6–12) — EPSS + CISA KEV enrichment (
core/enrichment.py), bulk triage → OpenVEX + ignore files (core/suppressions.py), dependency-free dashboard charts (core/trends.py), terminal-graphics capability detection (Phase 9), AI-assisted "explain a finding" (core/ai_triage.py, Phase 10), reachability heuristic (core/reachability.py, Phase 12). Phase 11 (web Console) was deliberately not pursued — see #274.Browser viewer (B0/B1/B2/B4) — design tokens + a
prefers-reduced-motion-gated motion layer (View Transitions, count-up, draw-on), real inline-SVG charts (core/svg_charts.py, shared with the report), a Cmd/Ctrl-K command palette, the opt-in EPSS/KEV Risk column (reusingcore/enrichment), a persistent sticky scan-context bar, and the formal report (core/report.pyUI-free model →/reportHTML +/report.pdfvia WeasyPrint behind the opt-in[report]extra; ADR-031). Reachability is deliberately terminal-only (it scans source).argusTTY-gated so headless/CI is untouched; every new browser feature (Risk column, PDF) is opt-in.Testing
Pilot: sidebar, scan runner, Fix overlay, Settings retheme, Config toggle+save, Init flow; browser routes via FastAPITestClient; report HTML/PDF; light + dark verified)Test Results
--cov-fail-under=80). Every sub-PR (feat(console): Argus Console — bare-argus home launcher + live settings #262–feat(browser): persistent sticky scan-context bar (B0 IA) #280) merged green in CI.TestClient, so the interactive code is covered in CI without the[terminal]/[browser]extras. The PDF edge is tested via an injected renderer (WeasyPrint not in the default env).Security Considerations
argus.ymlwithout a second keypress.GET. Enrichment (EPSS/KEV) and the PDF report are opt-in; enrichment sends only public CVE ids and caches on disk; reachability (source scanning) is kept out of the browser by design. WeasyPrint isolated behind the opt-in[report]extra.AI Context Updates (.ai/)
.ai/architecture.yamlupdated (all newcore/+viewers/modules, in both the main map and docsite mirror).ai/workflows.yamlupdated.ai/decisions.yamlupdated (ADR-031 — formal report: UI-free model, HTML-first, server-side PDF behind[report]).ai/errors.yamlupdatedChecklist
docs/console.md,docs/view-terminal.md,docs/view-browser.md,docs/developer/CONSOLE-ROADMAP.md,docs/developer/BROWSER-ROADMAP.md)Related Issues
The Argus Console epic (
docs/developer/CONSOLE-ROADMAP.md) + the browser viewer & reporting epic (docs/developer/BROWSER-ROADMAP.md). Sub-PRs: #262–#280.